package com.ys.utils.aliyun;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PolicyConditions;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.ys.utils.base.j2se.Logger;
import com.ys.utils.base.j2se.StringHandler;

import java.io.File;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
import java.util.UUID;

/**
 * @ 名称: oss上传工具
 * @ 返回值:
 */
public class OssHandler {

    private String accessId;
    private String accessKey;
    private String endpoint;
    private String bucket;
    private String host;
    private String domain;
    private String roleArn;
    
    /**
     * 构造助手
     * @param accessId OSS的AccessKeyId
     * @param accessKey OSS的AccessKeySecret
     * @param endpoint OSS的endpoint
     * @param bucket OSS的bucketname 
     * @param domain 域名路径 https://aloss.11oi.com/
     */
    public OssHandler(String accessId, String accessKey, String endpoint, String bucket, String domain,String roleArn){
    	this.accessId = accessId;
    	this.accessKey = accessKey;
    	this.endpoint = endpoint;
    	this.bucket = bucket;
    	this.domain = domain;
    	this.host = "https://"+bucket+"."+endpoint; //host的格式为https://+BUCKET+.ENDPOINT
		this.roleArn = roleArn;
    	check();
    }
    
    public OssHandler(Map<String, String> propertiesProxy,int type){
		this(propertiesProxy, false,type);
    }

    public OssHandler(Map<String, String> propertiesProxy, boolean isPrivate,int type){
    	this.accessId = propertiesProxy.get("oss.access.id");
    	this.accessKey = propertiesProxy.get("oss.access.key");
    	this.endpoint = propertiesProxy.get("oss.endpoint");
    	if(isPrivate && Logger.isOnline()) {
    		this.endpoint = propertiesProxy.get("oss.private.endpoint");
    	}
		if(type==1) {
			this.bucket = propertiesProxy.get("oss.bucket");
			this.domain = propertiesProxy.get("oss.path");
		}else{
			this.bucket = propertiesProxy.get("oss.bucket."+type);
			this.domain = propertiesProxy.get("oss.path."+type);
		}
    	this.host = "https://"+bucket+"."+endpoint;
    	this.roleArn = propertiesProxy.get("oss.roleArn");
    	check();
    }
    
    private void check(){
    	if(StringHandler.isEmpty(accessId)){
    		throw new RuntimeException("OssHandler check accessId isEmpty");
        }
    	if(StringHandler.isEmpty(accessKey)){
    		throw new RuntimeException("OssHandler check accessKey isEmpty");
    	}
    	if(StringHandler.isEmpty(endpoint)){
    		throw new RuntimeException("OssHandler check endpoint isEmpty");
    	}
    	if(StringHandler.isEmpty(bucket)){
    		throw new RuntimeException("OssHandler check bucket isEmpty");
    	}
    	if(StringHandler.isEmpty(domain)){
    		throw new RuntimeException("OssHandler check domain isEmpty");
    	}
		if(StringHandler.isEmpty(roleArn)){
			throw new RuntimeException("OssHandler check roleArn isEmpty");
		}
    }
    
    /**
     * 获取存储授权令牌信息（默认1分钟过期）
     * @param filePrefix 授权路径
     */
    public com.ys.utils.aliyun.OssInfo info(String filePrefix){
    	return info(filePrefix, 60);
    }
    
    /**
     * 获取存储授权令牌信息
     * @param filePrefix 授权路径
     * @param expireTime 过期时间
     */
    public com.ys.utils.aliyun.OssInfo info(String filePrefix, long expireTime){
    	OSSClient client = null;
    	try {
			//设置过期时间
			client = new OSSClient(endpoint, accessId, accessKey);
			long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
			Date expiration = new Date(expireEndTime);
			//组装获取令牌入参
			PolicyConditions policyConds = new PolicyConditions();
			policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
			policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, filePrefix);
			//解析获取令牌信息
			String postPolicy = client.generatePostPolicy(expiration, policyConds);
			byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
			String encodedPolicy = BinaryUtil.toBase64String(binaryData);
			String postSignature = client.calculatePostSignature(postPolicy);
			
			//组装返回结果
			com.ys.utils.aliyun.OssInfo ossInfo = new com.ys.utils.aliyun.OssInfo();
			ossInfo.setAccessid(accessId);
			ossInfo.setPolicy(encodedPolicy);
			ossInfo.setSignature(postSignature);
			ossInfo.setHost(host);
			ossInfo.setDir(filePrefix);
			ossInfo.setExpire(expireEndTime / 1000);
			ossInfo.setPath(domain);
			ossInfo.setFileName(UUID.randomUUID().toString());
			return ossInfo;
		} catch (Exception e) {
			throw e;
		} finally {
			if(null != client){
				client.shutdown();
			}
		}
    }

	/**
	 * 获取存储授权令牌信息
	 * @param filePrefix 授权路径
	 * @param fileName 文件名
	 * @param expireTime 过期时间
	 */
	public com.ys.utils.aliyun.OssInfo info(String filePrefix, String fileName, long expireTime){
		OSSClient client = null;
		try {
			//设置过期时间
			client = new OSSClient(endpoint, accessId, accessKey);
			long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
			Date expiration = new Date(expireEndTime);
			//组装获取令牌入参
			PolicyConditions policyConds = new PolicyConditions();
			policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
			policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, filePrefix);
			//解析获取令牌信息
			String postPolicy = client.generatePostPolicy(expiration, policyConds);
			byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
			String encodedPolicy = BinaryUtil.toBase64String(binaryData);
			String postSignature = client.calculatePostSignature(postPolicy);

			//组装返回结果
			com.ys.utils.aliyun.OssInfo ossInfo = new com.ys.utils.aliyun.OssInfo();
			ossInfo.setAccessid(accessId);
			ossInfo.setPolicy(encodedPolicy);
			ossInfo.setSignature(postSignature);
			ossInfo.setHost(host);
			ossInfo.setDir(filePrefix);
			ossInfo.setExpire(expireEndTime / 1000);
			ossInfo.setPath(domain);
			fileName = StringHandler.isNotEmpty(fileName)?fileName:UUID.randomUUID().toString();
			ossInfo.setFileName(fileName);
			return ossInfo;
		} catch (Exception e) {
			throw e;
		} finally {
			if(null != client){
				client.shutdown();
			}
		}
	}

	/**
	 * 获取sts临时授权
	 */
	public com.ys.utils.aliyun.OssSts sts(){
		DefaultAcsClient client = null;
		String policy = "{\n" +
				"    \"Version\": \"1\", \n" +
				"    \"Statement\": [\n" +
				"        {\n" +
				"            \"Action\": [\n" +
				"                \"oss:PutObject\"\n" +
				"            ], \n" +
				"            \"Resource\": [\n" +
				"                \"acs:oss:*:*:"+this.bucket+"/*\" \n" +
				"            ], \n" +
				"            \"Effect\": \"Allow\"\n" +
				"        }\n" +
				"    ]\n" +
				"}";
		try{
			DefaultProfile.addEndpoint("","Sts",endpoint.replace("oss-cn","sts.cn"));
			IClientProfile profile = DefaultProfile.getProfile("", accessId, accessKey);
			client = new DefaultAcsClient(profile);
			final AssumeRoleRequest request = new AssumeRoleRequest();
			request.setMethod(MethodType.POST);
			request.setRoleArn(roleArn);
			request.setRoleSessionName("app");
			request.setPolicy(policy); // 如果policy为空，则用户将获得该角色下所有权限。
			request.setDurationSeconds(3600L); // 设置临时访问凭证的有效时间为3600秒。
			final AssumeRoleResponse response = client.getAcsResponse(request);
			OssSts sts = new com.ys.utils.aliyun.OssSts();
			sts.setKey(response.getCredentials().getAccessKeyId());
			sts.setSecret(response.getCredentials().getAccessKeySecret());
			sts.setToken(response.getCredentials().getSecurityToken());
			sts.setExpiration(response.getCredentials().getExpiration());
			sts.setEndPoint(endpoint);
			sts.setBucketName(bucket);
			sts.setPath(domain);
			return sts;
		} catch (Exception e) {
			Logger.error("获取oss临时授权失败",e);
			return new com.ys.utils.aliyun.OssSts();
		} finally {
			if(null != client){
				client.shutdown();
			}
		}
	}

    /**
     * 上传文件
     * @param path 上传的完整路径，例如 abc/efg/123.jpg
     * @param file 上传的文件
     * @param contentType 文件类型 application/javascript text/css image/gif image/png image/jpeg text/html
     * @return 完成的url路径 https://xxx/xxx/123.jpg
     */
    public String putObject(String path, File file, String contentType){
    	OSSClient client = null;
    	try {
			// 创建PutObjectRequest对象。
			PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, path, file);
			// 如果需要上传时设置存储类型与访问权限，请参考以下示例代码。
			 ObjectMetadata metadata = new ObjectMetadata();
			// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
			// metadata.setObjectAcl(CannedAccessControlList.Private);
			metadata.setContentType(contentType);
			putObjectRequest.setMetadata(metadata);
			// 创建OSSClient实例。
			client = new OSSClient(endpoint, accessId, accessKey);
			// 上传文件。
			client.putObject(putObjectRequest);
			// 关闭OSSClient。
			client.shutdown();
			return domain + path;
		} catch (Exception e) {
			throw e;
		} finally {
			if(null != client){
				client.shutdown();
			}
		}
    }

    /**
     * 上传流
     * @param path 上传的完整路径，例如 abc/efg/123.jpg
     * @param input 上传的流
     * @param contentType 文件类型 application/javascript text/css image/gif image/png image/jpeg text/html
     * @return 完成的url路径 https://xxx/xxx/123.jpg
     */
    public String putObject(String path, InputStream input, String contentType){
    	OSSClient client = null;
    	try {
    		// 创建PutObjectRequest对象。
    		PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, path, input);
    		// 如果需要上传时设置存储类型与访问权限，请参考以下示例代码。
    		ObjectMetadata metadata = new ObjectMetadata();
    		// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
    		// metadata.setObjectAcl(CannedAccessControlList.Private);
    		metadata.setContentType(contentType);
    		putObjectRequest.setMetadata(metadata);
    		// 创建OSSClient实例。
    		client = new OSSClient(endpoint, accessId, accessKey);
    		// 上传文件。
    		client.putObject(putObjectRequest);
    		// 关闭OSSClient。
    		client.shutdown();
    		return domain + path;
    	} catch (Exception e) {
    		throw e;
    	} finally {
    		if(null != client){
    			client.shutdown();
    		}
    	}
    }
	/**
	 * @ 名称:删除oss上文件
	 * @ 创建人: dhf
	 * @ 参数： yourObjectName
	 * yourObjectName: 传过来的文件url
	 * 列如: http://8080/a/b/c/297964290899854.html
	 * 你应该截取得到后面的路径: a/b/c/297964290899854.html
	 * @ 创建时间: 2020/8/24
	 * @ 修改人和其它信息
	 * @ 返回值: java.lang.String
	 */
	public void deleteObject(String yourObjectName) {
		// 创建OSSClient实例。
		OSSClient client = null;
		try {
			client = new OSSClient(endpoint, accessId, accessKey);
            // 删除文件。
            client.deleteObject(bucket, yourObjectName.replace(this.domain,""));
		} catch (Exception e) {
			throw e;
		}finally {
			// 关闭OSSClient。
			if(null != client){
				client.shutdown();
			}
		}
	}

	/**
	 * @ 名称:删除oss上文件
	 * @ 创建人: dhf
	 * @ 参数： yourObjectName
	 * @ 参数： domain
	 * @ 创建时间: 2020/8/25
	 * @ 修改人和其它信息
	 * @ 返回值: void
	 */
	public void deleteObject(String yourObjectName,String domain) {
		this.deleteObject(yourObjectName.replace(domain,""));
	}
}
